﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DL.Tools
{
    /// <summary>
    /// 游戏物体对象池
    /// </summary>
    public class GameObjectPoolSystem : Singleton<GameObjectPoolSystem>
    {
        [SerializeField] private List<GameObjectAssets> assetsList = new List<GameObjectAssets>();
        [SerializeField] private Transform poolObjectParent;
        private Dictionary<Type, List<GameObject>> pools = new Dictionary<Type, List<GameObject>>();

        protected override void Construct()
        {
            StartCoroutine(InitPool());
        }

        protected override void Release()
        {
            ReleasePool(true);
        }

        private IEnumerator InitPool()
        {
            if (assetsList.Count == 0) yield break;

            //首先遍历外面配置的资源
            for (int i = 0; i < assetsList.Count; i++)
            {
                yield return null;
                var type = Type.GetType(assetsList[i].assetsName);
                if (type == null)
                {
                    Debugger.EO_LogError($"对象池实例化错误，未存在{assetsList[i].assetsName}类型，请检查！");
                    continue;
                }

                //检查列表第一个元素的内容是否已经在池子里面了，没有的话就创建一个
                if (!pools.ContainsKey(type))
                {
                    pools.Add(type, new List<GameObject>());
                }

                if (assetsList[i].parent == null)
                {
                    assetsList[i].parent = new GameObject(assetsList[i].assetsName).transform;
                }

                var parent = assetsList[i].parent;
                parent.SetParent(poolObjectParent);
                //创建完毕后，遍历这个对象的总数，比如总数5，那么就创建5个，然后存进字典
                for (int j = 0; j < assetsList[i].count; j++)
                {
                    CreateGameObject(type);
                }
            }
        }

        /// <summary>
        /// 回收整个对象池
        /// </summary>
        /// <param name="unrecoveredObject">是否回收正在使用的物体</param>
        public void ReleasePool(bool unrecoveredObject = false)
        {
            var names = new List<Type>();

            foreach (var pool in pools)
            {
                var count = pool.Value.Count;
                //出队并入队
                for (int i = 0; i < count;)
                {
                    var item = pool.Value[i];
                    if (unrecoveredObject || item.GetComponent<IPool>().IsRecycle)
                    {
                        pool.Value.Remove(item);
                        Destroy(item.gameObject);
                        count--;
                        continue;
                    }

                    i++;
                }

                if (count <= 0)
                {
                    names.Add(pool.Key);
                }
            }

            foreach (var name in names)
            {
                pools.Remove(name);
            }
        }

        /// <summary>
        /// 获取对应脚本
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T TakeGameObject<T>(Transform user = null) where T : IPool
        {
            var type = typeof(T);
            if (!pools.ContainsKey(type)) return default;
            var item = FindOneRecycleGameObject<T>();
            var script = item.GetComponent<T>();
            item.SetActive(true);
            script.SpawnObject(user);
            return script;
        }

        public T TakeGameobject<T>(Vector3 position, Quaternion rotation, Transform user = null) where T : IPool
        {
            var type = typeof(T);
            if (!pools.ContainsKey(type)) return default;
            var item = FindOneRecycleGameObject<T>();
            var script = item.GetComponent<T>();
            item.transform.position = position;
            item.transform.rotation = rotation;
            item.SetActive(true);
            script.SpawnObject(user);
            return script;
        }

        public void RecyleGameObject(GameObject go)
        {
            go.transform.position = Vector3.zero;
            go.transform.rotation = Quaternion.identity;
            go.SetActive(false);
        }

        /// <summary>
        /// 用于存量不足扩展
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private GameObject CreateGameObject<T>() where T : IPool
        {
            var type = typeof(T);

            if (!pools.ContainsKey(type)) pools.Add(type, new());

            var go = new GameObject(type.Name + "_" + (pools[type].Count + 1), type);
            //存放到他们的父节点上
            go.transform.SetParent(assetsList.Find(x => x.assetsName == type.Name).parent);
            go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
            var ipool = go.GetComponent<T>();
            ipool.IsRecycle = true;
            pools[type].Add(go);
            return go;
        }

        private IPool CreateGameObject(Type type)
        {
            var go = new GameObject(type.Name + "_" + (pools[type].Count + 1), type);
            //存放到他们的父节点上
            go.transform.SetParent(assetsList.Find(x => x.assetsName == type.Name).parent);
            go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
            pools[type].Add(go);
            var ipool = go.GetComponent(type) as IPool;
            ipool.IsRecycle = true;
            return ipool;
        }

        /// <summary>
        /// 找到一个可用物体
        /// </summary>
        /// <returns></returns>
        private GameObject FindOneRecycleGameObject<T>() where T : IPool
        {
            var type = typeof(T);
            foreach (var item in pools[type])
            {
                var ipool = item.GetComponent<T>();
                if (ipool.IsRecycle)
                {
                    return item;
                }
            }

            //出现在这里相当于没得了
            return CreateGameObject<T>();
        }

        [System.Serializable]
        private class GameObjectAssets
        {
            public string assetsName;
            public int count;
            public Transform parent;
        }


        // private void Update()
        // {
        //     if (Input.GetKeyDown(KeyCode.A))
        //     {
        //         ReleasePool(true);
        //     }
        //     
        //     if (Input.GetKeyDown(KeyCode.S))
        //     {
        //         TakeGameObject<Timer>().CreateTime(10, () => { Debugger.EO_Log("这是一个计时器"); });
        //     }
        //
        //     if (Input.GetKeyDown(KeyCode.D))
        //     {
        //         ReleasePool();
        //     }
        // }
    }

}
